package com.lh.java.connect;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

/****
 * 1：实现了 javax.sql.DataSource  接口
2：从连接池中获得的连接，在调用 Connection#close 时并不是真正意义上的关闭连接，而是将其还回到池中去
3：池中的连接被数据库服务端关闭时，这些连接该如何处理
4：连接回收问题。如果设了最大 50 个连接，在并发量高时正好用到这 50 个连接，如果高并发期过后，假如只有 10 个连接被用到，那剩下的 40 个应该要被回收，不能占用这些宝贵的资源
5：如果网络中断后又重新连接上，池中的连接该如何处理，这与 3 是类似的，涉及一个池中连接的健康检查机制 
 * 数据库接连池
 */
public class ConnectionPool
{
    private static int InitSize;// 初始化连接大小
    private static int MaxSize;// 最大连接数
    private static int IncreaseSize;// 自动增加大小
    private static Vector<PooledConnection> Conns;// 连接对象
    private static String URL;// 连接字符串
    private static String User;// 用户名
    private static String PassWord;// 密码

    private static int LeaveTime;// 连接允许空间时间
    private static int PoolRefTime;// 连接池回收间隔时间
    private static int TimeOut;// 连接超时自动断开

    /**
     * 连接超时自动断开
     */
    public void setTimeOut(int timeOut)
    {
	TimeOut = timeOut;
    }

    /**
     * 连接允许空间时间
     */
    public void setLeaveTime(int leaveTime)
    {
	LeaveTime = leaveTime;
    }

    /**
     * 连接池回收间隔时间
     * */
    public void setPoolRefTime(int poolRefTime)
    {
	PoolRefTime = poolRefTime;
    }

    /**
     * 初始化连接大小
     **/
    public void setInitSize(int initSize)
    {
	InitSize = initSize;
    }

    /**
     * 最大连接数
     */
    public void setMaxSize(int maxSize)
    {
	MaxSize = maxSize;
    }

    /**
     * 自动增加大小
     */
    public void setIncreaseSize(int increaseSize)
    {
	IncreaseSize = increaseSize;
    }

    public void setConns(Vector<PooledConnection> conns)
    {
	Conns = conns;
    }

    /**
     * 连接字符串
     */
    public void setURL(String uRL)
    {
	URL = uRL;
    }

    /**
     * 用户名
     */
    public void setUser(String user)
    {
	User = user;
    }

    /**
     * 密码
     */
    public void setPassWord(String passWord)
    {
	PassWord = passWord;
    }

    private static long getTime()
    {
	return new Date().getTime();
    }

    /**
     * 初始化连接池
     */
    public synchronized void initPool()
    {
	// 确保连接没有被创建
	if (Conns != null)
	{
	    return;// 终止
	}
	Conns = new Vector<PooledConnection>();
	this.createPool(InitSize);// 建立初始数量的连接
	new Recovery().start();
    }

    /**
     * 创建一个数据库连接
     */
    private Connection newConnection()
    {
	Connection conn = null;
	try
	{
	    conn = DriverManager.getConnection(URL, User, PassWord);
	    if (Conns.size() == 0)// 如果当前连接池是空的
	    {
		DatabaseMetaData meta = conn.getMetaData();
		int dbConnMax = meta.getMaxConnections();// 返回数据库允许的最大连接
		System.out.print("数据库最大允许的连接数量可能为:" + dbConnMax);
		System.out.println(" (0为不受限制)");
		if ((dbConnMax > 0) && (MaxSize > dbConnMax))
		{
		    // 如果连接数量最大值超出数据库允许的数量，重置最大连接值
		    this.setMaxSize(dbConnMax);
		}
	    }
	} catch (SQLException e)
	{
	    // TODO Auto-generated catch block
	    System.out.println("连接数据库出错:" + e.toString());
	}
	return conn;// 返回创建的数据库连接
    }

    /**
     * 创建连接池
     */
    public void createPool(int size)
    {
	System.out.println("*** 扩充连接池 ***");
	// 循环创建连接，并填充到连接对象
	for (int i = 0; i < size; i++)
	{
	    if (Conns.size() > MaxSize)// 如果连接数量已超出最大值
	    {
		System.out.println("-- 连接已超出最大值");
		break;// 退出
	    }
	    // 将连接对象填充到集合
	    Conns.addElement(new PooledConnection(newConnection()));
	}
	System.out.println("增加连接数量,当前连接数量：" + Conns.size());

    }

    /**
     * 遍历对象，返回一个可用的连接
     * */
    private Connection findConns()
    {
	// 连接对象
	Connection conn = null;
	// 连接池对象
	PooledConnection poolConn = null;
	// 连接对象迭代
	Enumeration<PooledConnection> enumer = Conns.elements();
	while (enumer.hasMoreElements())
	{
	    poolConn = (PooledConnection) enumer.nextElement();// 返回集合中的连接对象
	    if (!poolConn.isBuy())// 如果该连接没有被占用
	    {
		System.out.println("找到空闲连接..");
		conn = poolConn.getConn();// 获取连接对象
		poolConn.setBuy(true);// 标记该连接被占用
		break;// 找到连接后退出
	    }
	}
	if (conn == null)
	{
	    System.out.println("等待空闲连接释放..");
	    wait(3000);// 等待
	    if (conn == null)// 如果还是没有获取到可用连接，
	    {
		System.out.println("没有找到空闲连接，创建新的连接..");
		createPool(IncreaseSize);// 尝试创建一定数量的连接
		conn = findConns();// 调用本身
	    }
	}
	return conn;
    }

    /**
     * 返回一个可用连接
     */
    public synchronized Connection getConnection()
    {
	Connection conn = this.findConns();
	System.out.println("当前连接数量:" + Conns.size());
	return conn;
    }

    /***
     * 返回并释放连接
     */
    public void returnConn(Connection conn)
    {
	// 确保连接池存在
	if (Conns == null)
	{
	    System.out.println("连接池不存在，无法释放");
	    return;// 终止
	}
	// 遍历
	PooledConnection poolConn = null;
	Enumeration<PooledConnection> enumer = Conns.elements();
	while (enumer.hasMoreElements())
	{
	    poolConn = (PooledConnection) enumer.nextElement();// 返回集合中的连接对象
	    if (conn == poolConn.getConn())// 如果连接池中存在该对象
	    {
		// 设置连接为空闲
		poolConn.setBuy(false);
		System.out.println("返回连接 --- 当前连接数量:" + Conns.size());
		break;
	    }
	}
    }

    /***
     * 刷新连接池
     */
    private synchronized void refConnection()
    {
	if (Conns == null)
	{
	    System.out.print("连接池不存在，无法刷新");
	    return;// 终止
	}
	// 执行刷新操作
	PooledConnection poolConn = null;
	Enumeration<PooledConnection> enumer = Conns.elements();
	while (enumer.hasMoreElements())// 获取连接对象
	{
	    poolConn = (PooledConnection) enumer.nextElement();// 返回集合中的连接对象
	    long dif = getTime() - poolConn.getBuyTime();
	    if ((poolConn.isBuy()) && (dif > TimeOut))// 查看连接是否被占用(如果很长一段时间连接都被占用，可能出现故障，必须更替该连接)
	    {
		// 关闭并替换该连接
		System.out.println("替换连接对象..");
		this.close(poolConn.getConn());
		poolConn.setConn(newConnection());
		poolConn.setBuy(false);// 设置该连接为空闲
	    } else
	    // 如果连接对象闲置，且已经创建很久，关掉它
	    {
		long diff = getTime() - poolConn.getLeave();
		if (diff > LeaveTime)
		{
		    System.out.println("关闭闲置的连接对象...");
		    this.close(poolConn.getConn());
		    Conns.removeElement(poolConn);
		}
	    }
	}
	System.out.println("####当前连接数量:" + Conns.size());
    }

    /**
     * 关闭连接
     */
    public void close(Connection conn)
    {
	try
	{
	    conn.close();
	} catch (SQLException e)
	{
	    // TODO Auto-generated catch block
	    System.out.println("关闭连接出错：" + e.toString());
	}
    }

    /**
     * 清空连接池
     */
    public synchronized void clear()
    {
	if (Conns == null)
	{
	    System.out.println("连接池不存在，无法关闭");
	    return;// 终止
	}
	// 执行关闭
	PooledConnection poolConn = null;
	Enumeration<PooledConnection> enumer = Conns.elements();
	while (enumer.hasMoreElements())
	{
	    poolConn = (PooledConnection) enumer.nextElement();
	    if (poolConn.isBuy())// 如果连接忙
	    {
		this.wait(3000);// 等待3秒
		// 关闭连接
		this.close(poolConn.getConn());
		Conns.removeElement(enumer);// 移除对象
	    }
	}
	// 设置连接池为空
	Conns = null;
    }

    // 设置等待时间
    private void wait(int seconds)
    {
	try
	{
	    Thread.sleep(seconds);
	} catch (InterruptedException e)
	{
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
    }

    /**
     * 连接对象类
     */
    class PooledConnection
    {
	boolean isBuy;// 连接对象是否被占用
	Connection conn;// 连接对象
	long Leave;// 已闲置时间
	long BuyTime;// 被占用时间

	public long getLeave()
	{
	    return Leave;
	}

	public long getBuyTime()
	{
	    return BuyTime;
	}

	public PooledConnection(Connection conns)
	{
	    this.conn = conns;
	    this.Leave = getTime();
	}

	public boolean isBuy()
	{
	    return isBuy;
	}

	public void setBuy(boolean buy)
	{
	    this.isBuy = buy;
	    if (buy)
		this.BuyTime = getTime();// 标记被占用开始时间
	    else
		this.Leave = getTime();// 标记空闲开始时间
	}

	public Connection getConn()
	{
	    return conn;
	}

	public void setConn(Connection conn)
	{
	    this.conn = conn;
	}
    }

    /**
     * 异步刷新连接池回收资源
     */
    class Recovery extends Thread
    {
	public void run()
	{
	    // 每隔一定的时间，处理连接池中的连接对象
	    while (true)
	    {
		try
		{
		    Thread.sleep(PoolRefTime);
		    refConnection();// 刷新连接池
		} catch (InterruptedException e)
		{
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
	    }
	}
    }
}